package org.videolan.libvlc;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.daimc.vlcdemo.UniversalMediaController;
import com.daimc.vlcdemo.UniversalMediaController.MediaPlayerControl;
import org.videolan.libvlc.OrientationDetector.Direction;
import org.videolan.libvlc.OrientationDetector.OrientationChangeListener;

import org.videolan.vlc.util.VLCInstance;
import org.videolan.vlc.util.WeakHandler;

import java.util.Locale;

public class PlayerView extends SurfaceView implements IVideoPlayer,
		OrientationChangeListener, MediaPlayerControl {

	private static final String TAG = "PlayerView";

	public interface OnChangeListener {

		public void onBufferChanged(float buffer);

		public void onLoadComplet();

		public void onError();

		public void onEnd();

		public void onScaleChange(boolean isFull);
	}

	private Context mContext;
	private LibVLC mLibVLC;

	// Whether fallback from HW acceleration to SW decoding was done.
	private boolean mDisabledHardwareAcceleration = false;
	private int mPreviousHardwareAccelerationMode;

	private SurfaceHolder mSurfaceHolder;
	// private SurfaceHolder mSubtitlesSurfaceHolder;

	// size of the video
	private int mVideoHeight;
	private int mVideoWidth;

	private OnChangeListener mOnChangeListener;
	private boolean mCanSeek = false;
	
	private boolean mAutoRotation = true;
	private String url;

	private UniversalMediaController mMediaController;
	private boolean mFitXY = true;

	private OrientationDetector mOrientationDetector;
	private Handler mHandler;

	public PlayerView(Context context) {
		this(context, null);
	}

	public PlayerView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
		init();
	}

	public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		mContext = context;
		init();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		if (mFitXY) {
			onMeasureFitXY(widthMeasureSpec, heightMeasureSpec);
		} else {
			onMeasureKeepAspectRatio(widthMeasureSpec, heightMeasureSpec);
		}
	}

	private void onMeasureFitXY(int widthMeasureSpec, int heightMeasureSpec) {
		int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
		int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
		setMeasuredDimension(width, height);
	}

	private void onMeasureKeepAspectRatio(int widthMeasureSpec,
			int heightMeasureSpec) {
		// Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) +
		// ", "
		// + MeasureSpec.toString(heightMeasureSpec) + ")");

		int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
		int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
		if (mVideoWidth > 0 && mVideoHeight > 0) {

			int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
			int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
			int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
			int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

			if (widthSpecMode == MeasureSpec.EXACTLY
					&& heightSpecMode == MeasureSpec.EXACTLY) {
				// the size is fixed
				width = widthSpecSize;
				height = heightSpecSize;

				// for compatibility, we adjust size based on aspect ratio
				if (mVideoWidth * height < width * mVideoHeight) {
					// Log.i("@@@", "image too wide, correcting");
					width = height * mVideoWidth / mVideoHeight;
				} else if (mVideoWidth * height > width * mVideoHeight) {
					// Log.i("@@@", "image too tall, correcting");
					height = width * mVideoHeight / mVideoWidth;
				}
			} else if (widthSpecMode == MeasureSpec.EXACTLY) {
				// only the width is fixed, adjust the height to match aspect
				// ratio if possible
				width = widthSpecSize;
				height = width * mVideoHeight / mVideoWidth;
				if (heightSpecMode == MeasureSpec.AT_MOST
						&& height > heightSpecSize) {
					// couldn't match aspect ratio within the constraints
					height = heightSpecSize;
				}
			} else if (heightSpecMode == MeasureSpec.EXACTLY) {
				// only the height is fixed, adjust the width to match aspect
				// ratio if possible
				height = heightSpecSize;
				width = height * mVideoWidth / mVideoHeight;
				if (widthSpecMode == MeasureSpec.AT_MOST
						&& width > widthSpecSize) {
					// couldn't match aspect ratio within the constraints
					width = widthSpecSize;
				}
			} else {
				// neither the width nor the height are fixed, try to use actual
				// video size
				width = mVideoWidth;
				height = mVideoHeight;
				if (heightSpecMode == MeasureSpec.AT_MOST
						&& height > heightSpecSize) {
					// too tall, decrease both width and height
					height = heightSpecSize;
					width = height * mVideoWidth / mVideoHeight;
				}
				if (widthSpecMode == MeasureSpec.AT_MOST
						&& width > widthSpecSize) {
					// too wide, decrease both width and height
					width = widthSpecSize;
					height = width * mVideoHeight / mVideoWidth;
				}
			}
		} else {
			// no size yet, just adopt the given spec sizes
		}
		setMeasuredDimension(width, height);
	}

	public void initPlayer(String url) {
		try {
			mLibVLC = VLCInstance.getLibVlcInstance();
		} catch (LibVlcException e) {
			e.printStackTrace();
		}
		mLibVLC.getMediaList().remove(0);
		mLibVLC.playMRL(url);
		this.url = url;
	}

	private void init() {
		try {
			if (mLibVLC == null) {
				mLibVLC = VLCInstance.getLibVlcInstance();
			}
		} catch (LibVlcException e) {
			throw new RuntimeException("PlayerView Init Failed");
		}
		// video view
		mHandler = new Handler();
		mSurfaceHolder = this.getHolder();
		mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
		mSurfaceHolder.addCallback(mSurfaceCallback);
	}

	private final Callback mSurfaceCallback = new Callback() {
		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			if (format == PixelFormat.RGBX_8888) {
				Log.d(TAG, "Pixel format is RGBX_8888");
			} else if (format == PixelFormat.RGB_565) {
				Log.d(TAG, "Pixel format is RGB_565");
			} else if (format == ImageFormat.YV12) {
				Log.d(TAG, "Pixel format is YV12");
			} else {
				Log.d(TAG, "Pixel format is other/unknown,format number is "
						+ format);
			}
			if (mLibVLC != null) {
				mLibVLC.attachSurface(holder.getSurface(), PlayerView.this);
			}
		}

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			enableOrientationDetect();
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			if (mLibVLC != null)
				mLibVLC.detachSurface();
			disableOrientationDetect();
		}
	};
	

	private void enableOrientationDetect() {
		if (mOrientationDetector == null) {
			mOrientationDetector = new OrientationDetector(mContext);
			mOrientationDetector.setOrientationChangeListener(PlayerView.this);
			mOrientationDetector.enable();
		}
	}

	private void disableOrientationDetect() {
		if (mOrientationDetector != null) {
			mOrientationDetector.disable();
		}
	}

	@Override
	public void setSurfaceSize(int width, int height, int visible_width,
			int visible_height, int sar_num, int sar_den) {
		Log.e("setSurfaceSize   " ,"w--" + width + ",h--" + height + ",vw--"
				+ visible_width + ",vh--" + visible_height);
		if (width * height == 0) {
			return;
		}
		// store video size
		mVideoHeight = height;
		mVideoWidth = width;
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				changeSurfaceSize();
			}
		});
	}

	public void setOnChangeListener(OnChangeListener listener) {
		mOnChangeListener = listener;
	}

	public void changeSurfaceSize() {
		getHolder().setFixedSize(mVideoWidth, mVideoHeight);
		invalidate();
	}

	public void eventHardwareAccelerationError() {
		EventHandler em = EventHandler.getInstance();
		em.callback(EventHandler.HardwareAccelerationError, new Bundle());
	}

	private void handleHardwareAccelerationError() {
		mLibVLC.stop();
		mDisabledHardwareAcceleration = true;
		mPreviousHardwareAccelerationMode = mLibVLC.getHardwareAcceleration();
		mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
		startVlc();
	}

	public void startVlc() {
		if (mLibVLC == null) {
			return;
		}
		mLibVLC.eventVideoPlayerActivityCreated(true);
		EventHandler.getInstance().addHandler(eventHandler);
		mLibVLC.playIndex(0);
		setKeepScreenOn(true);

		/*
		 * WARNING: hack to avoid a crash in mediacodec on KitKat. Disable
		 * hardware acceleration if the media has a ts extension.
		 */
		if (LibVlcUtil.isKitKatOrLater()) {
			String locationLC = url.toLowerCase(Locale.ENGLISH);
			if (locationLC.endsWith(".ts") || locationLC.endsWith(".tts")
					|| locationLC.endsWith(".m2t")
					|| locationLC.endsWith(".mts")
					|| locationLC.endsWith(".m2ts")) {
				mDisabledHardwareAcceleration = true;
				mPreviousHardwareAccelerationMode = mLibVLC
						.getHardwareAcceleration();
				mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
			}
		}

		// 关闭硬件加速
		mDisabledHardwareAcceleration = true;
		mPreviousHardwareAccelerationMode = mLibVLC.getHardwareAcceleration();
		mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
	};

	public void setMediaController(UniversalMediaController controller) {
		if (mMediaController != null) {
			mMediaController.hide();
		}
		mMediaController = controller;
		attachMediaController();
	}

	private void attachMediaController() {
		if (mLibVLC != null && mMediaController != null) {
			mMediaController.setMediaPlayer(this);
			mMediaController.setEnabled(true);
			mMediaController.hide();
		}
	}

	public void playVlc() {
		mLibVLC.play();
		setKeepScreenOn(false);
	};

	public void pauseVlc() {
		mLibVLC.pause();
		setKeepScreenOn(false);
	};

	public void stopVlc() {
		mLibVLC.stop();
		setKeepScreenOn(false);
	}

	public void releaseVlc() {
		EventHandler em = EventHandler.getInstance();
		em.removeHandler(eventHandler);
		// MediaCodec opaque direct rendering should not be used anymore since
		// there is no surface to attach.
		mLibVLC.eventVideoPlayerActivityCreated(false);
		if (mDisabledHardwareAcceleration) {
			mLibVLC.setHardwareAcceleration(mPreviousHardwareAccelerationMode);
		}
	}

	public long getTime() {
		return mLibVLC.getTime();
	}

	public long getLength() {
		return mLibVLC.getLength();
	}

	public void setTime(long time) {
		mLibVLC.setTime(time);
	}

	public void setNetWorkCache(int time) {
		mLibVLC.setNetworkCaching(time);
	}

	public String pathToUrl(String path) {
		return LibVLC.PathToURI(path);
	}

	public boolean canSeekable() {
		return mCanSeek;
	}

	public boolean isPlaying() {
		return mLibVLC.isPlaying();
	}

	public boolean isSeekable() {
		return mLibVLC.isSeekable();
	}

	public int getPlayerState() {
		return mLibVLC.getPlayerState();
	}

	public int getVolume() {
		return mLibVLC.getVolume();
	}

	public void setVolume(int volume) {
		mLibVLC.setVolume(volume);
	}

	public void setCanRotate(boolean rotate) {
		this.mAutoRotation =rotate;
	}

	public void seek(int delta) {
		// unseekable stream
		if (mLibVLC.getLength() <= 0 || !mCanSeek)
			return;

		long position = mLibVLC.getTime() + delta;
		if (position < 0)
			position = 0;
		mLibVLC.setTime(position);
	}

	private final Handler eventHandler = new VideoPlayerHandler(this);

	private static class VideoPlayerHandler extends WeakHandler<PlayerView> {
		public VideoPlayerHandler(PlayerView owner) {
			super(owner);
		}

		@Override
		public void handleMessage(Message msg) {
			PlayerView playerView = getOwner();
			if (playerView == null)
				return;
			switch (msg.getData().getInt("event")) {
			case EventHandler.MediaPlayerNothingSpecial:
				break;
			case EventHandler.MediaPlayerOpening:
				playerView.mMediaController.showLoading();
				break;
			case EventHandler.MediaParsedChanged:
				break;
			case EventHandler.MediaPlayerPlaying:
				if (playerView.mOnChangeListener != null) {
					playerView.mOnChangeListener.onLoadComplet();
				}
				break;
			case EventHandler.MediaPlayerPaused:
				playerView.mMediaController.showComplete();
				break;
			case EventHandler.MediaPlayerStopped:
				playerView.mMediaController.showComplete();
				break;
			case EventHandler.MediaPlayerEndReached:
				if (playerView.mOnChangeListener != null) {
					playerView.mOnChangeListener.onEnd();
				}
				playerView.mMediaController.showError();
				break;
			case EventHandler.MediaPlayerVout:
				playerView.mMediaController.hideLoading();
				break;
			case EventHandler.MediaPlayerPositionChanged:
				if (!playerView.mCanSeek) {
					playerView.mCanSeek = true;
				}
				break;
			case EventHandler.MediaPlayerEncounteredError:
				if (playerView.mOnChangeListener != null) {
					playerView.mOnChangeListener.onError();
					playerView.mMediaController.showError();
				}
				break;
			case EventHandler.HardwareAccelerationError:
				if (playerView.mOnChangeListener != null
						&& playerView.mDisabledHardwareAcceleration) {
					playerView.stopVlc();
					playerView.mOnChangeListener.onError();
					playerView.mMediaController.showError();
				} else {
					playerView.handleHardwareAccelerationError();
				}
				break;
			case EventHandler.MediaPlayerTimeChanged:
				// avoid useless error logs

				break;
			case EventHandler.MediaPlayerBuffering:
				if (playerView.mOnChangeListener != null) {
					playerView.mOnChangeListener.onBufferChanged(msg.getData()
							.getFloat("data"));
				}
				if (msg.getData().getFloat("data") >= 100) {
					playerView.mMediaController.hideLoading();
					if (!playerView.isSeekable()) {
						playerView.setVolume(0);
					}
				} else {
					playerView.mMediaController.showLoading((int) msg.getData()
							.getFloat("data"));
				}
				break;
			default:
				Log.d(TAG, String.format("Event not handled (0x%x)", msg
						.getData().getInt("event")));
				break;
			}
		}
	}

	@Override
	public void onOrientationChanged(int screenOrientation, Direction direction) {
		if (!mAutoRotation) {
			return;
		}
		if (direction == OrientationDetector.Direction.PORTRAIT) {
			setFullscreen(false, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		} else if (direction == OrientationDetector.Direction.REVERSE_PORTRAIT) {
			setFullscreen(false,
					ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
		} else if (direction == OrientationDetector.Direction.LANDSCAPE) {
			setFullscreen(true, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		} else if (direction == OrientationDetector.Direction.REVERSE_LANDSCAPE) {
			setFullscreen(true,
					ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
		}
	};

	public void setFullscreen(boolean fullscreen, int screenOrientation) {
		// Activity需要设置为:
		// android:configChanges="keyboardHidden|orientation|screenSize"
		Activity activity = (Activity) mContext;
		if (fullscreen) {
			ViewGroup.LayoutParams params = getLayoutParams();
			mVideoWidth = params.width;// 保存全屏之前的参数
			mVideoHeight = params.height;
			activity.getWindow().addFlags(
					WindowManager.LayoutParams.FLAG_FULLSCREEN);
			activity.setRequestedOrientation(screenOrientation);
		} else {
			ViewGroup.LayoutParams params = getLayoutParams();
			params.width = mVideoWidth;// 使用全屏之前的参数
			params.height = mVideoHeight;
			setLayoutParams(params);
			activity.getWindow().clearFlags(
					WindowManager.LayoutParams.FLAG_FULLSCREEN);
			activity.setRequestedOrientation(screenOrientation);
		}
		mMediaController.toggleButtons(fullscreen);
		if (mOnChangeListener != null) {
			mOnChangeListener.onScaleChange(fullscreen);
		}
	}

	@Override
	public int getDuration() {
		return (int) mLibVLC.getLength();
	}

	@Override
	public int getCurrentPosition() {
		return (int) mLibVLC.getTime();
	}

	@Override
	public void seekTo(int pos) {
		seek(pos);
	}

	@Override
	public int getBufferPercentage() {
		return 0;
	}

	@Override
	public boolean canPause() {
		return mLibVLC.isSeekable();
	}

	@Override
	public boolean canSeekBackward() {
		return mLibVLC.isSeekable();
	}

	@Override
	public boolean canSeekForward() {
		return  mLibVLC.isSeekable();
	}

	@Override
	public void closePlayer() {
		stopVlc();
	}

	@Override
	public void setFullscreen(boolean fullscreen) {
		int screenOrientation = fullscreen ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
				: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
		setFullscreen(fullscreen, screenOrientation);
	}

	@Override
	public void start() {
		playVlc();
	}

	@Override
	public void pause() {
		pauseVlc();
	}

}
